home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / netprog.zip / NETPROG.TAR / rcmd / rshd.c < prev    next >
C/C++ Source or Header  |  1989-12-17  |  15KB  |  585 lines

  1. /*
  2.  * Copyright (c) 1983, 1988 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that the above copyright notice and this paragraph are
  7.  * duplicated in all such forms and that any documentation,
  8.  * advertising materials, and other materials related to such
  9.  * distribution and use acknowledge that the software was developed
  10.  * by the University of California, Berkeley.  The name of the
  11.  * University may not be used to endorse or promote products derived
  12.  * from this software without specific prior written permission.
  13.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  14.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  15.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  16.  */
  17.  
  18. #ifndef lint
  19. char copyright[] =
  20. "@(#) Copyright (c) 1983, 1988 The Regents of the University of California.\n\
  21.  All rights reserved.\n";
  22. #endif /* not lint */
  23.  
  24. #ifndef lint
  25. static char sccsid[] = "@(#)rshd.c    5.17.1.2 (Berkeley) 2/7/89";
  26. #endif /* not lint */
  27.  
  28. /*
  29.  * Remote shell server.  We're invoked by the rcmd(3) function.
  30.  */
  31.  
  32. #include    <sys/param.h>
  33. #include    <sys/ioctl.h>
  34. #include    <sys/socket.h>
  35. #include    <sys/file.h>
  36. #include    <sys/time.h>
  37. #include    <netinet/in.h>
  38. #include    <arpa/inet.h>
  39. #include    <stdio.h>
  40. #include    <varargs.h>
  41. #include    <pwd.h>
  42. #include    <signal.h>
  43. #include    <netdb.h>
  44. #include    <syslog.h>
  45. #include    <errno.h>
  46. extern int    errno;
  47.  
  48. char    *index();
  49. char    *rindex();
  50. char    *strncat();
  51.  
  52. int    keepalive = 1;        /* flag for SO_KEEPALIVE socket option */
  53. int    one = 1;        /* used for setsockopt() and ioctl() */
  54.  
  55. char    env_user[20]  = "USER=";    /* the environment strings we set */
  56. char    env_home[64]  = "HOME=";
  57. char    env_shell[64] = "SHELL=";
  58. char    *env_ptrs[] =
  59.         {env_home, env_shell, "PATH=/usr/ucb:/bin:/usr/bin:", env_user, 0};
  60. char    **environ;
  61.  
  62. /*ARGSUSED*/
  63. main(argc, argv)
  64. int    argc;
  65. char    **argv;
  66. {
  67.     int            ch, addrlen;
  68.     struct sockaddr_in    cli_addr;
  69.     struct linger        linger;
  70.     extern int        opterr, optind;        /* in getopt() */
  71.     extern int        _check_rhosts_file;    /* in validuser() */
  72.  
  73.     openlog("rsh", LOG_PID | LOG_ODELAY, LOG_DAEMON);
  74.  
  75.     opterr = 0;
  76.     while ( (ch = getopt(argc, argv, "ln")) != EOF)
  77.         switch((char) ch) {
  78.         case 'l':
  79.             _check_rhosts_file = 0;    /* don't check .rhosts file */
  80.             break;
  81.  
  82.         case 'n':
  83.             keepalive = 0;        /* don't enable SO_KEEPALIVE */
  84.             break;
  85.  
  86.         case '?':
  87.         default:
  88.             syslog(LOG_ERR, "usage: rshd [-l]");
  89.             break;
  90.         }
  91.  
  92.     argc -= optind;
  93.     argv += optind;
  94.  
  95.     /*
  96.      * We assume we're invoked by inetd, so the socket that the connection
  97.      * is on, is open on descriptors 0, 1 and 2.
  98.      *
  99.      * First get the Internet address of the client process.
  100.      * This is required for all the authentication we perform.
  101.      */
  102.  
  103.     addrlen = sizeof(cli_addr);
  104.     if (getpeername(0, (struct sockaddr *) &cli_addr, &addrlen) < 0) {
  105.         fprintf(stderr, "%s: ", argv[0]);
  106.         perror("getpeername");
  107.         _exit(1);
  108.     }
  109.  
  110.     /*
  111.      * Set the socket options: SO_KEEPALIVE and SO_LINGER.
  112.      */
  113.  
  114.     if (keepalive &&
  115.         setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *) &one,
  116.                                   sizeof(one)) < 0)
  117.         syslog(LOG_WARNING, "setsockopt(SO_KEEPALIVE): %m");
  118.  
  119.     linger.l_onoff = 1;
  120.     linger.l_linger = 60;
  121.     if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *) &linger,
  122.                sizeof(linger)) < 0)
  123.         syslog(LOG_WARNING, "setsockopt(SO_LINGER): %m");
  124.  
  125.     doit(&cli_addr);
  126.         /* doit() never returns */
  127. }
  128.  
  129. doit(cli_addrp)
  130. struct sockaddr_in    *cli_addrp;    /* client's Internet address */
  131. {
  132.     int            sockfd2, pipefd[2], childpid,
  133.                 maxfdp1, cc, oursecport;
  134.     fd_set            ready, readfrom;
  135.     short            clisecport;
  136.     char            *cp, *hostname;
  137.     char            servuname[16], cliuname[16], cmdbuf[NCARGS+1];
  138.     char            remotehost[2 * MAXHOSTNAMELEN + 1];
  139.     char            buf[BUFSIZ], c, sigval;
  140.     struct passwd        *pwd;
  141.     struct hostent        *hp;
  142.  
  143.     signal(SIGINT, SIG_DFL);
  144.     signal(SIGQUIT, SIG_DFL);
  145.     signal(SIGTERM, SIG_DFL);
  146.  
  147. #ifdef DEBUG
  148.     {
  149.         int t = open("/dev/tty", 2);
  150.         if (t >= 0) {
  151.             ioctl(t, TIOCNOTTY, (char *) 0);
  152.             close(t);
  153.         }
  154.     }
  155. #endif
  156.  
  157.     /*
  158.      * Verify that the client's address is an Internet address.
  159.      */
  160.  
  161.     if (cli_addrp->sin_family != AF_INET) {
  162.         syslog(LOG_ERR, "malformed from address\n");
  163.         exit(1);
  164.     }
  165.  
  166. #ifdef IP_OPTIONS
  167.     {
  168.     u_char        optbuf[BUFSIZ/3], *optptr;
  169.     char        lbuf[BUFSIZ], *lptr;
  170.     int        optsize, ipproto;
  171.     struct protoent    *ip;
  172.  
  173.     if ( (ip = getprotobyname("ip")) != NULL)
  174.         ipproto = ip->p_proto;
  175.     else
  176.         ipproto = IPPROTO_IP;
  177.  
  178.     optsize = sizeof(optbuf);
  179.     if (getsockopt(0, ipproto, IP_OPTIONS, (char *) optbuf, &optsize) == 0
  180.         && optsize != 0) {
  181.         /*
  182.          * The client has set IP options.  This isn't allowed.
  183.          * Use syslog() to record the fact.
  184.          */
  185.  
  186.         lptr = lbuf;
  187.         optptr = optbuf;
  188.         for ( ; optsize > 0; optptr++, optsize--, lptr += 3)
  189.             sprintf(lptr, " %2.2x", *optptr);
  190.                 /* print each option byte as 3 ASCII chars */
  191.         syslog(LOG_NOTICE,
  192.             "Connection received using IP options (ignored): %s", lbuf);
  193.  
  194.         /*
  195.          * Turn off the options.  If this doesn't work, we quit.
  196.          */
  197.  
  198.         if (setsockopt(0, ipproto, IP_OPTIONS,
  199.                     (char *) NULL, &optsize) != 0) {
  200.             syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m");
  201.             exit(1);
  202.         }
  203.     }
  204.     }
  205. #endif
  206.  
  207.     /*
  208.      * Verify that the client's address was bound to a reserved port.
  209.      */
  210.  
  211.     cli_addrp->sin_port = ntohs((u_short) cli_addrp->sin_port);
  212.                 /* need host byte ordered port# to compare */
  213.     if (cli_addrp->sin_port >= IPPORT_RESERVED  ||
  214.         cli_addrp->sin_port <  IPPORT_RESERVED/2) {
  215.         syslog(LOG_NOTICE, "Connection from %s on illegal port",
  216.                     inet_ntoa(cli_addrp->sin_addr));
  217.         exit(1);
  218.     }
  219.  
  220.     /*
  221.      * Read the ASCII string specifying the secondary port# from
  222.      * the socket.  We set a timer of 60 seconds to do this read,
  223.      * else we assume something is wrong.  If the client doesn't want
  224.      * the secondary port, they just send the terminating null byte.
  225.      */
  226.  
  227.     alarm(60);
  228.     clisecport = 0;
  229.     for ( ; ; ) {
  230.         if ( (cc = read(0, &c, 1)) != 1) {
  231.             if (cc < 0)
  232.                 syslog(LOG_NOTICE, "read: %m");
  233.             shutdown(0, 2);
  234.             exit(1);
  235.         }
  236.         if (c == 0)        /* null byte terminates the string */
  237.             break;
  238.         clisecport = (clisecport * 10) + (c - '0');
  239.     }
  240.     alarm(0);
  241.  
  242.     if (clisecport != 0) {
  243.         /*
  244.          * If the secondary port# is nonzero, then we have to
  245.          * connect to that port (which the client has already
  246.          * created and is listening on).  The secondary port#
  247.          * that the client tells us to connect to has to also be
  248.          * a reserved port#.  Also, our end of this secondary
  249.          * connection has to also have a reserved TCP port bound
  250.          * to it, plus.
  251.          */
  252.  
  253.         if (clisecport >= IPPORT_RESERVED) {
  254.             syslog(LOG_ERR, "2nd port not reserved\n");
  255.             exit(1);
  256.         }
  257.  
  258.         oursecport = IPPORT_RESERVED - 1; /* starting port# to try */
  259.         if ( (sockfd2 = rresvport(&oursecport)) < 0) {
  260.             syslog(LOG_ERR, "can't get stderr port: %m");
  261.             exit(1);
  262.         }
  263.  
  264.         /*
  265.          * Use the cli_addr structure that we already have.
  266.          * The 32-bit Internet address is obviously that of the
  267.          * client's, just change the port# to the one specified
  268.          * by the client as the secondary port.
  269.          */
  270.  
  271.         cli_addrp->sin_port = htons((u_short) clisecport);
  272.         if (connect(sockfd2, (struct sockaddr *) cli_addrp,
  273.                 sizeof(*cli_addrp)) < 0) {
  274.             syslog(LOG_INFO, "connect second port: %m");
  275.             exit(1);
  276.         }
  277.     }
  278.  
  279.     /*
  280.      * Get the "name" of the client from its Internet address.
  281.      * This is used for the authentication below.
  282.      */
  283.  
  284.     hp = gethostbyaddr((char *) &cli_addrp->sin_addr,
  285.                 sizeof(struct in_addr), cli_addrp->sin_family);
  286.     if (hp) {
  287.         /*
  288.          * If the name returned by gethostbyaddr() is in our domain,
  289.          * attempt to verify that we haven't been fooled by someone
  290.          * in a remote net.  Look up the name and check that this
  291.          * address corresponds to the name.
  292.          */
  293.  
  294.         if (local_domain(hp->h_name)) {
  295.             strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1);
  296.             remotehost[sizeof(remotehost) - 1] = 0;
  297.             if ( (hp = gethostbyname(remotehost)) == NULL) {
  298.                 syslog(LOG_INFO,
  299.                     "Couldn't look up address for %s",
  300.                                     remotehost);
  301.                 my_error("Couldn't look up addr for your host");
  302.                 exit(1);
  303.             }
  304.             for ( ; ; hp->h_addr_list++) {
  305.                 if (bcmp(hp->h_addr_list[0],
  306.                               (caddr_t) &cli_addrp->sin_addr,
  307.                               sizeof(cli_addrp->sin_addr)) == 0)
  308.                     break;    /* equal, OK */
  309.  
  310.                 if (hp->h_addr_list[0] == NULL) {
  311.                     syslog(LOG_NOTICE,
  312.                         "Host addr %s not listed for host %s",
  313.                             inet_ntoa(cli_addrp->sin_addr),
  314.                             hp->h_name);
  315.                     my_error("Host address mismatch");
  316.                     exit(1);
  317.                 }
  318.             }
  319.         }
  320.         hostname = hp->h_name;
  321.     } else
  322.         hostname = inet_ntoa(cli_addrp->sin_addr);
  323.  
  324.     /*
  325.      * Read three strings from the client.
  326.      */
  327.  
  328.     getstr(cliuname, sizeof(cliuname), "cliuname");
  329.     getstr(servuname, sizeof(servuname), "servuname");
  330.     getstr(cmdbuf, sizeof(cmdbuf), "command");
  331.  
  332.     /*
  333.      * Look up servuname in the password file.  The servuname has
  334.      * to be a valid account on this system.
  335.      */
  336.  
  337.     setpwent();
  338.     if ( (pwd = getpwnam(servuname)) == (struct passwd *) NULL) {
  339.         my_error("Login incorrect.\n");
  340.         exit(1);
  341.     }
  342.     endpwent();
  343.  
  344.     /*
  345.      * We'll execute the client's command in the home directory
  346.      * of servuname.
  347.      */
  348.  
  349.     if (chdir(pwd->pw_dir) < 0) {
  350.         chdir("/");
  351. #ifdef notdef
  352.         my_error("No remote directory.\n");
  353.         exit(1);
  354. #endif
  355.     }
  356.  
  357.     if (pwd->pw_passwd != NULL  &&  *pwd->pw_passwd != '\0'  &&
  358.         ruserok(hostname, pwd->pw_uid == 0, cliuname, servuname) < 0) {
  359.         my_error("Permission denied.\n");
  360.         exit(1);
  361.     }
  362.  
  363.     /*
  364.      * If the servuname isn't root, then check if logins are disabled.
  365.      */
  366.  
  367.     if (pwd->pw_uid != 0  &&  access("/etc/nologin", F_OK) == 0) {
  368.         my_error("Logins currently disabled.\n");
  369.         exit(1);
  370.     }
  371.  
  372.     /*
  373.      * Now write the null byte back to the client telling it
  374.      * that everything is OK.
  375.      * Note that this means that any error messages that we generate
  376.      * from now on (such as the perror() if the execl() fails), won't
  377.      * be seen by the rcmd() function, but will be seen by the
  378.      * application that called rcmd() when it reads from the socket.
  379.      */
  380.  
  381.     if (write(2, "", 1) != 1)
  382.         exit(1);
  383.  
  384.     if (clisecport) {
  385.         /*
  386.          * We need a secondary channel.  Here's where we create
  387.          * the control process that'll handle this secondary
  388.          * channel.
  389.          * First create a pipe to use for communication between
  390.          * the parent and child, then fork.
  391.          */
  392.  
  393.         if (pipe(pipefd) < 0) {
  394.             my_error("Can't make pipe.\n");
  395.             exit(1);
  396.         }
  397.  
  398.         if ( (childpid = fork()) == -1) {
  399.             my_error("Try again.\n");
  400.             exit(1);
  401.         }
  402.  
  403.         if (pipefd[0] > sockfd2)    /* set max fd + 1 for select */
  404.             maxfdp1 = pipefd[0];
  405.         else
  406.             maxfdp1 = sockfd2;
  407.         maxfdp1++;
  408.  
  409.         if (childpid != 0) {
  410.             /*
  411.              * Parent process == control process.
  412.              * We: (1) read from the pipe and write to sockfd2;
  413.              *     (2) read from sockfd2 and send corresponding
  414.              *       signal.
  415.              */
  416.  
  417.             close(0);    /* child handles the original socket */
  418.             close(1);    /* (0, 1, and 2 were from inetd) */
  419.             close(2);
  420.             close(pipefd[1]);    /* close write end of pipe */
  421.  
  422.             FD_ZERO(&readfrom);
  423.             FD_SET(sockfd2, &readfrom);
  424.             FD_SET(pipefd[0], &readfrom);
  425.  
  426.             ioctl(pipefd[0], FIONBIO, (char *) &one);
  427.                     /* should set sockfd2 nbio! */
  428.             do {
  429.                 ready = readfrom;
  430.                 if (select(maxfdp1, &ready, (fd_set *) 0,
  431.                     (fd_set *) 0, (struct timeval *) 0) < 0)
  432.                           /* wait until something to read */
  433.                     break;
  434.  
  435.                 if (FD_ISSET(sockfd2, &ready)) {
  436.                     if (read(sockfd2, &sigval, 1) <= 0)
  437.                         FD_CLR(sockfd2, &readfrom);
  438.                     else
  439.                         killpg(childpid, sigval);
  440.                 }
  441.  
  442.                 if (FD_ISSET(pipefd[0], &ready)) {
  443.                     errno = 0;
  444.                     cc = read(pipefd[0], buf, sizeof(buf));
  445.                     if (cc <= 0) {
  446.                         shutdown(sockfd2, 2);
  447.                         FD_CLR(pipefd[0], &readfrom);
  448.                     } else
  449.                         write(sockfd2, buf, cc);
  450.                 }
  451.             } while (FD_ISSET(sockfd2, &readfrom) ||
  452.                  FD_ISSET(pipefd[0], &readfrom));
  453.             /*
  454.              * The pipe will generate an EOF when the shell
  455.              * terminates.  The socket will terminate when the
  456.              * client process terminates.
  457.              */
  458.  
  459.             exit(0);
  460.         }
  461.  
  462.         /*
  463.          * Child process.  Become a process group leader, so that
  464.          * the control process above can send signals to all the
  465.          * processes we may be the parent of.  The process group ID
  466.          * (the getpid() value below) equals the childpid value from
  467.          * the fork above.
  468.          */
  469.  
  470.         setpgrp(0, getpid());
  471.         close(sockfd2);        /* control process handles this fd */
  472.         close(pipefd[0]);    /* close read end of pipe */
  473.         dup2(pipefd[1], 2);    /* stderr of shell has to go through
  474.                        pipe to control process */
  475.         close(pipefd[1]);
  476.     }
  477.  
  478.     if (*pwd->pw_shell == '\0')
  479.         pwd->pw_shell = "/bin/sh";
  480.  
  481.     /*
  482.      * Set the gid, then uid to become the user specified by "servuname".
  483.      */
  484.  
  485.     setgid((gid_t) pwd->pw_gid);
  486.     initgroups(pwd->pw_name, pwd->pw_gid);    /* BSD groups */
  487.     setuid((uid_t) pwd->pw_uid);
  488.  
  489.     /*
  490.      * Set up an initial environment for the shell that we exec().
  491.      */
  492.  
  493.     environ = env_ptrs;
  494.     strncat(env_home,  pwd->pw_dir,   sizeof(env_home)-6);
  495.     strncat(env_shell, pwd->pw_shell, sizeof(env_shell)-7);
  496.     strncat(env_user,  pwd->pw_name,  sizeof(env_user)-6);
  497.  
  498.     if ( (cp = rindex(pwd->pw_shell, '/')) != NULL)
  499.         cp++;            /* step past first slash */
  500.     else
  501.         cp = pwd->pw_shell;    /* no slash in shell string */
  502.  
  503.     execl(pwd->pw_shell, cp, "-c", cmdbuf, (char *) 0);
  504.  
  505.     perror(pwd->pw_shell);        /* error from execl() */
  506.     exit(1);
  507. }
  508.  
  509. /*
  510.  * Read a string from the socket.  Make sure it fits, else fatal error.
  511.  */
  512.  
  513. getstr(buf, cnt, errmesg)
  514. char    *buf;
  515. int    cnt;        /* sizeof() the char array */
  516. char    *errmesg;    /* in case error message required */
  517. {
  518.     char    c;
  519.  
  520.     do {
  521.         if (read(0, &c, 1) != 1)
  522.             exit(1);    /* error or EOF */
  523.         *buf++ = c;
  524.         if (--cnt == 0) {
  525.             my_error("%s too long.\n", errmesg);
  526.             exit(1);
  527.         }
  528.     } while (c != 0);    /* null byte terminates the string */
  529. }
  530.  
  531. /*
  532.  * Send an error message back to the rcmd() client.
  533.  * The first byte we send must be binary 1, followed by the ASCII
  534.  * error message, followed by a newline.
  535.  */
  536.  
  537. my_error(va_alist)
  538. va_dcl
  539. {
  540.     va_list        args;
  541.     char        *fmt, buff[BUFSIZ];
  542.  
  543.     va_start(args);
  544.     fmt = va_arg(args, char *);
  545.     buff[0] = 1;
  546.     vsprintf(buff + 1, fmt, args);
  547.     va_end(args);
  548.  
  549.     write(2, buff, strlen(buff));    /* fd 2 = socket, from inetd */
  550. }
  551.  
  552. /*
  553.  * Check whether the specified host is in our local domain, as determined
  554.  * by the part of the name following the first period, in its name and in ours.
  555.  * If either name is unqualified (contains no period), assume that the host
  556.  * is local, as it will be interpreted as such.
  557.  */
  558.  
  559. int                /* return 1 if local domain, else return 0 */
  560. local_domain(host)
  561. char    *host;
  562. {
  563.     register char    *ptr1, *ptr2;
  564.     char        localhost[MAXHOSTNAMELEN];
  565.  
  566.     if ( (ptr1 = index(host, '.')) == NULL)
  567.         return(1);        /* no period in remote host name */
  568.  
  569.     gethostname(localhost, sizeof(localhost));
  570.     if ( (ptr2 = index(localhost, '.')) == NULL)
  571.         return(1);        /* no period in local host name */
  572.  
  573.     /*
  574.      * Both host names contain a period.  Now compare both names,
  575.      * starting with the first period in each name (i.e., the names
  576.      * of their respective domains).  If equal, then the remote domain
  577.      * equals the local domain, return 1.
  578.      */
  579.  
  580.     if (strcasecmp(ptr1, ptr2) == 0)    /* case insensitive compare */
  581.         return(1);
  582.  
  583.     return(0);
  584. }
  585.